SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00004 1 08-24-9413:21ALL KAI ROHRBACHER Dpmi HiRes Timer SWAG9408 ü⌐òk 40 «u πUNIT asytimer;π{Purpose : High resolution timer which runs asynchronous to the }π{ rest of the program }π{Author : Kai Rohrbacher, kai.rohrbacher@logo.ka.sub.org }π{Language : BorlandPascal 7.0 }π{Date : 26.06.1994 }π{Remarks : - Runs both in real- and protected mode. }π{ - Only available on AT-style machines or better (uses }π{ real time clock services) }π{ - Will "fall through" on PC's transparently: behaves as }π{ if time ran off immediately}ππINTERFACEππVAR TimeFlag:^BYTE;ππFUNCTION ATClockAvailable:BOOLEAN;πPROCEDURE SetCycleTime(microseconds:LONGINT);πFUNCTION TimeOver:BOOLEAN;π INLINE($C4/$1E/TimeFlag/ {LES BX,TimeFlag}π $26/$8A/$07/ {MOV AL,ES:[BX] }π $B1/$07/ {MOV CL,7 }π $D2/$E8); {SHR AL,CL}πPROCEDURE Trigger;ππIMPLEMENTATIONππUSES CRT;ππ{$IFDEF DPMI}πTYPE Treg=RECORD {stuff for that dumb DPMI-server}π CASE BYTE OFπ 0:(LoLo,LoHi,HiLo,HiHi:BYTE);π 1:(Lo16,Hi16:WORD);π END;π Tregisters32=π RECORDπ EDI,ESI,EBP,junk32,EBX,EDX,ECX,EAX:Treg;π Flags32,ES,DS,FS,GS,IP,CS,SP,SS:WORDπ END;πVAR regs32:Tregisters32;ππ FUNCTION EmulateInt(IntNr:BYTE; VAR regs32:Tregisters32):BOOLEAN;π ASSEMBLER; {emulate real mode interrupt IntNr with registers regs32}π ASMπ MOV AX,300h {emulate INT}π XOR BH,BH {no A20 gate reset, please}π MOV BL,IntNr {INT to emulate}π XOR CX,CX {no parameter passing via PM stack}π LES DI,regs32 {pointer to register set}π INT 31h {go for it}π CMC {carry flag set if error, reflect this}π MOV AX,0 {as a BOOLEAN value: return TRUE if C=0}π ADC AX,AX {and FALES otherwise}π END;π{$ENDIF}ππVAR CycleTimeLo16,CycleTimeHi16:WORD;π IsAT:BYTE;ππ{$IFDEF DPMI}πFUNCTION ATClockAvailable:BOOLEAN; {protected mode function}πBEGINπ TimeFlag^:=0; {reset flag}π FillChar(regs32,SizeOf(regs32),0);π regs32.ECX.Lo16:=0;π regs32.EDX.Lo16:=1; {trigger flag after 1us}π regs32.ES :=$40; {_segment_ address of Timeflag}π regs32.EBX.Lo16:=Ofs(TimeFlag^); {offset part = $F0}π regs32.EAX.Lo16:=$8300;ππ IF NOT EmulateInt($15,regs32)π THEN WRITELN('Something went wrong in the INT-emulation!?');ππ Delay(1); {INT-emulation went ok, look for timer event:}π {wait 1000us, so event must have happened:}π {Flag now should have been set to $80:}π ATClockAvailable:=TimeFlag^=$80;πEND;ππ{$ELSE}ππFUNCTION ATClockAvailable:BOOLEAN; {real mode function}πBEGINπ TimeFlag^:=0; {reset flag}π IF Test8086<>0 {is it at least an AT?}π THEN ASM {yes, have a closer look:}π STIπ XOR CX,CX {trigger after 1us}π MOV DX,1π LES BX,TimeFlag {set Flag to $80 after this time}π MOV AX,8300h {run asynchron to rest of program}π INT 15h {go!}π END;π Delay(1); {wait a 1000us}π ATClockAvailable:=TimeFlag^=$80 {Flag=$80, if it worked}πEND;π{$ENDIF}ππPROCEDURE SetCycleTime(microseconds:LONGINT);πBEGINπ TimeFlag^:=$80;π CycleTimeHi16:=microseconds SHR 16;π CycleTimeLo16:=microseconds AND $FFFF;π IF (microseconds<>0) AND ATClockAvailableπ THEN IsAT:=0 {ja, Zeitüberwachung soll benutzt werden }π ELSE IsAT:=$80 {nein, keine möglich oder nicht gewünscht }πEND;ππPROCEDURE Trigger;π{starts timer, which must have previously been set by SetCycleTime()}πBEGINπ IF IsAT<>0 THEN EXIT; {jmp out, if timer services unavailable}π TimeFlag^:=0;π{$IFDEF DPMI}π regs32.ECX.Lo16:=CycleTimeHi16;π regs32.EDX.Lo16:=CycleTimeLo16; {trigger flag after t us}π regs32.ES :=$40; {_segment_ address of Timeflag}π regs32.EBX.Lo16:=Ofs(TimeFlag^); {offset part = $F0}π regs32.EAX.Lo16:=$8300;ππ IF NOT EmulateInt($15,regs32)π THEN WRITELN('Something went wrong in the INT-emulation!?');π{$ELSE}πASMπ MOV CX,CycleTimeHi16π MOV DX,CycleTimeLo16π LES BX,TimeFlag {set Flag to $80 after this time}π MOV AX,8300h {run asynchron to rest of program}π INT 15h {go!}πEND;π{$ENDIF}πEND;ππBEGINπ TimeFlag:=Ptr(Seg0040,$F0); {available byte in 1st MB}π SetCycleTime(0)πEND.ππ____ππPROGRAM TestUnit_asytimer;π{Kai Rohrbacher, kai.rohrbacher@logo.ka.sub.org}πUSES asytimer;πCONST wait:LONGINT=5000000; {trigger time in us -> 5sec}ππ PROCEDURE SomeThing;π CONST s:ARRAY[0..3] OF CHAR='\|/-';π help:BYTE=0;π BEGIN WRITE(s[help]+^H); help:=(help+1) AND 3 END;ππBEGINπ IF ATClockAvailableπ THEN WRITELN('INT15h-timer-routine available!')π ELSE WRITELN('INT15h-timer-routine doesn''t work!');ππ SetCycleTime(wait);π WRITELN('Between the following 2 bells, there should be a delay of ',π wait,' microseconds');π Trigger; {wait 5s = 5000ms}π WRITE(#7);π WHILE NOT TimeOver DO SomeThing;π WRITELN(#7'Done!');πEND.π 2 08-24-9413:44ALL PETE ROCCA multitasking program SWAG9408 /äMü 16 «u {πYC> Does anyone got any unit/code on giving up time slice under DV or OS/2?πHere they are for DOS, Windows, OS/2, DV and DoubleDos. You will needπto detect the enviroment first (although none should make the systemπhang if it's the wrong enviroment, just be ignored) The key to goodπidle release is finding the right spots to put them. I have gotten myπdoor making unit that I created to about 97% idle during pauses and 93%πidle while waiting for keyboard input (with no delay in response - muchπbetter than the typical 12% idle pauses and 8% idle keyboard waits)πHere is how... }ππProcedure Sleep(Seconds: Word);πVarπ H,M,S,T,Last: Word;πBeginπ If Seconds = 0 Then Exit;π If Seconds > 999 Then Seconds := Seconds DIV 1000;π {incase of caller is thinking milliseconds}ππ GetTime(H,M,Last,T);π Repeatπ Repeatπ GetTime(H,M,S,T);π TimerSlice;π TimerSlice;π Until S <> Last;π Last := S;π Dec(Seconds);π Until Seconds = 0;πEnd;ππFunction GetChar: Char;πVarπ Counter, Span: Byte;π Done: Boolean;πBeginπ Span := 0;π Done := False;π Repeatπ Inc(Counter);π If Counter > Span Thenπ Beginπ Counter := 0;π If IsChar Then Done := Trueπ Else If Span < 50 Then Inc(Span);π Endπ Else TimerSlice;π Until Done;π If KeyPressedExtended Then GetChar := Readkeyπ Else GetChar := RxChar;πEnd;ππProcedure TimerSlice;πBeginπ Case SystemEnviroment Ofπ DOS4:;π DOS5,π WINDOWS,π OS2: Asmπ MOV AX,$1680π INT $2Fπ End;π DV: Asmπ MOV AX,$1000π INT $15π End;π DOUBLEDOS: Asmπ MOV AX,$EE01π INT $21π End;π End;πEnd;ππThis is released as public domain, however if you are using it, I'dπappreciate a PRIVATE NETMAIL message to me at 1:2401/123 letting meπknow.ππThanksππPete RoccaπMultiboard Communications Centre @ 1:2401/123π 3 08-24-9417:52ALL GAYLE DAVIS A good Timer unit SWAG9408 ß'╦R 19 «u πUNIT Timer;πINTERFACEππTYPEπ tTimerObject = objectπ TimerTicks : LONGINT;π MaxSeconds : LONGINT;π PROCEDURE Start(Amount : LONGINT);π FUNCTION ElapsedSeconds : LONGINT;π FUNCTION Remaining : LONGINT;π FUNCTION Expired : BOOLEAN;π FUNCTION PrintableTimer(Tics : LONGINT) : STRING;π END;ππIMPLEMENTATIONπVARπ TicksSinceMidnight : LONGINT ABSOLUTE $0040 : $006c;ππ PROCEDURE tTimerObject.Start(Amount : LONGINT);π BEGINπ TimerTicks := TicksSinceMidnight;π MaxSeconds := Amount;π END;ππ FUNCTION tTimerObject.ElapsedSeconds : LONGINT; { elapsed time in seconds }π CONSTπ TicksPerDay = 1573040;π TicksPerSecond = 18.20648;π VARπ ElapsedTicks : LONGINT;ππ BEGINπ ElapsedTicks := TicksSinceMidnight;π IF (ElapsedTicks >= TimerTicks) THENπ ElapsedTicks := ElapsedTicks - TimerTicksπ ELSE { Midnight rollover occurred }π ElapsedTicks := TicksPerDay - TimerTicks + ElapsedTicks;π ElapsedSeconds := ROUND (ElapsedTicks / TicksPerSecond);π END;ππ FUNCTION tTimerObject.Expired : BOOLEAN; { Has this timer expired ?? }π BEGINπ Expired := (ElapsedSeconds > MaxSeconds);π END;ππ FUNCTION tTimerObject.Remaining : LONGINT; { How many seconds remain? }π BEGINπ IF Expired THEN Remaining := 0 ELSEπ Remaining := MaxSeconds - ElapsedSeconds;π END;ππ FUNCTION tTimerObject.PrintableTimer(Tics : LONGINT) : STRING;π { return a printable time string }ππ VARπ S, T : STRING;π Hour, Min, Sec, Time : LONGINT;π i : INTEGER;ππ BEGINπ Hour := (Tics div 3600);π Min := (Tics div 60);π Sec := Tics - (Min * 60);π STR(Min : 2, T);π IF T[1] = #32 THEN T[1] := '0';π S := T + ':';π STR(Sec : 2, T);π IF T[1] = #32 THEN T[1] := '0';π S := S + T;π PrintableTimer := S;π END;πEND.ππ{ ---------------------------- DEMO ----------------------- }ππusesπ CRT, Timer;πvarπ t : tTimerObject;ππbeginπ ClrScr;π t.Start(10); { set a 10 second timer }π GoToXY(1,1); Write(t.TimerTicks);π repeatπ GoToXY(1,2); Write(t.PrintableTimer(t.Remaining));π GoToXY(1,3); Write(t.PrintableTimer(t.ElapsedSeconds));π until (t.Expired); { wait until it expires }π Readkey;πend.π 4 08-25-9409:12ALL STEVE ROGERS Clock Ticks SWAG9408 É∩α╠ 8 «u {πPN>Hi, Steve. I know this has been asked before, but am thinkingπ >of using TicksSinceMidnight which is at memory location $0040:006Cπ >as you say. Ny question is to what kind of type (size) variable shouldπ >I assign this to, and how many bytes are reserved for this number?π >$0040:006C .. $0040:006D ????π >word? longint? integer?π .π 60 * 60 * 24 = 86400 seconds per day. . . a longint is needed.π}π varπ TickSinceMidnight : longint ABSOLUTE $0040:$006c;π{π BTW, that second $ is important :)πππ >I appreciate your help. I'm going to try to get a 10ths secondsπ >counter going by test-running the number of ticks in a second. Or wordsπ >to that effect.ππ There are approximately 18.2 ticks per second (thanks, Spock :)π}π